﻿// HSS.Interlink.Web.InterlinkHandler.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.html)
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2012
// ----------------------------------------------------------------------------
// File:       InterlinkHandler.cs
// Author:     HSS\gbanta
// Created:    07/16/2011
// Modified:   12/05/2011
// ----------------------------------------------------------------------------
namespace HSS.Interlink.Web
{
	#region Using Directives
	using System;
	using System.Web;
	using System.Web.SessionState;
	using System.Configuration;
	using System.Collections.Generic;
	#endregion

	#region InterlinkHandler
	/// <summary>
	/// Is the base HttpHandler of HSS Interlink.
	/// </summary>
	public abstract class InterlinkHandler : IHttpHandler, IRequiresSessionState
	{
		#region Constants
		/// <summary>
		/// HTTP STATUS 404: File Not Found
		/// </summary>
		protected const int StatusCode_File_Not_Found = 404;
		#endregion

		#region Fields
		private static Dictionary<string, Type> fileHandlers = new Dictionary<string, Type>();
		#endregion

		#region IHttpHandler
		/// <summary>
		/// Indicates if this Handler can stay alive and be reused or if its recreated for each request: False.
		/// </summary>
		public bool IsReusable
		{
			get { return false; }
		}
		/// <summary>
		/// Process incoming requests.
		/// </summary>
		/// <param name="context">The current HttpContext.</param>
		public abstract void ProcessRequest(HttpContext context);
		#endregion

		#region Methods
		/// <summary>
		/// Registers a file handler.
		/// </summary>
		/// <param name="key">A unique key to register the handler under.</param>
		/// <param name="fileHandler">The type file handler to register.</param>
		/// <remarks>
		/// <para>
		/// NOTE: will overwrite any file handler already registered with the same key.
		/// </para>
		/// </remarks>
		public static void RegisterFileHandler(string key, Type fileHandler)
		{
			fileHandlers[key] = fileHandler;
		}
		/// <summary>
		/// Registers a file handler.
		/// </summary>
		/// <param name="key">A unique key to register the handler under.</param>
		/// <param name="fullName">The type full name of the file handler to register.</param>
		/// <exception cref="ArgumentOutOfRangeException">File Handler could not be resolved</exception>
		/// <remarks>
		/// <para>
		/// NOTE: will overwrite any file handler already registered with the same key.
		/// </para>
		/// </remarks>
		public static void RegisterFileHandler(string key, string fullName)
		{
			var fileHandler = Type.GetType(fullName);
			if (null == fileHandler)
				throw new ArgumentOutOfRangeException("fullName", "File Handler could not be resolved.");
		}

		#endregion

		#region Protected
		/// <summary>
		/// Attempts to parse the request query string.
		/// </summary>
		/// <param name="context">The current HttpContext.</param>
		/// <param name="query">The populated FileQuery.</param>
		/// <returns>true if the query was successfully parsed; otherwise false.</returns>
		protected bool TryParseQuery(HttpContext context, out FileQuery query)
		{
			query = null;

			if (null == context || null == context.Request || null == context.Request.QueryString || context.Request.QueryString.Count != 1)
			{
				this.Fault(context, StatusCode_File_Not_Found);
				return false;
			}

			var queryString = context.Request.QueryString[0];
			if (string.IsNullOrEmpty(queryString))
			{
				this.Fault(context, StatusCode_File_Not_Found);
				return false;
			}

			query = FileQuery.ParseRequestQuery(queryString);
			if (null == query)
			{
				this.Fault(context, StatusCode_File_Not_Found);
				return false;
			}
			if (query.IsPing)
			{
				this.Pinged(context);
				return false; // DON'T CONTINUE PROCESSING
			}
			if (string.IsNullOrEmpty(query.HandlerKey))
			{
				this.Fault(context, StatusCode_File_Not_Found);
				return false;
			}

			return true;

		}
		/// <summary>
		/// When the request fails, clears the input stream and sets the response status code.
		/// </summary>
		/// <param name="context">The current HttpContext.</param>
		/// <param name="statusCode">The HttpStatusCode.</param>
		protected void Fault(HttpContext context, int statusCode)
		{
			context.Request.InputStream.Close();
			context.Response.Clear();
			context.Response.StatusCode = statusCode;
		}
		/// <summary>
		/// When the request is for a ping, returns a the ping response.
		/// </summary>
		/// <param name="context">The current HttpContext.</param>
		protected void Pinged(HttpContext context)
		{
			context.Request.InputStream.Close();
			context.Response.Clear();
			context.Response.Write(string.Format("<h1>PING: Current Time (UTC) = {0}</h1>", DateTime.UtcNow.ToString()));
		}
		/// <summary>
		/// Activates the requested handler.
		/// </summary>
		/// <typeparam name="THandler">The Type to activate.</typeparam>
		/// <param name="key">The key that identifies the Type to activate.</param>
		/// <returns>The activated Type.</returns>
		/// <remarks>
		/// <para>
		/// First attempts to locate the File Handler from the collection
		/// of programmatically registered handlers. If not found, then
		/// searches the web config app settings.
		/// </para>
		/// </remarks>
		protected THandler GetHandler<THandler>(string key)
		{
			Type t = null;
			if (!fileHandlers.TryGetValue(key, out t))
			{
				// Try web config
				var processor = ConfigurationManager.AppSettings[key];
				if (string.IsNullOrEmpty(processor))
					throw new InvalidOperationException(Properties.Resources.Upload_MissingUploadHandler);
				t = Type.GetType(processor, false);
			}
			if (null == t)
				throw new InvalidOperationException(Properties.Resources.Upload_FailedHandlerTypeResolution);
			return (THandler)Activator.CreateInstance(t);
		}
		#endregion
	}
	#endregion
}